Skip to content

fix(vmi): dynamic vci index + group-periodic share (raw vci) - #1112

Open
peanutchan wants to merge 9 commits into
hw-native-sys:mainfrom
peanutchan:feat/vmi-vci-dynamic-index-base
Open

fix(vmi): dynamic vci index + group-periodic share (raw vci)#1112
peanutchan wants to merge 9 commits into
hw-native-sys:mainfrom
peanutchan:feat/vmi-vci-dynamic-index-base

Conversation

@peanutchan

@peanutchan peanutchan commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Dynamic loop IVs (TileLang T.serial / scf.for) arrive as MLIR index; coerce to signless i32 in PTODSL so pto.vmi.vci ODS/verify accept the base.
  • Restore optional {group} on vci/iota (ODS + PTODSL group=), preserve through unified→legacy and rematerialize.
  • Semantics: without {group} → continuous ramp base..base+L-1 (VL128: 0..63 then 64..127). With {group=C} → group-periodic restart per group of size S = L/C.
  • Contiguous packing lowers as raw pto.vci(%chunkBase). Identical group runs share one physical index VL (return %idx, %idx for VL128/g=2).
  • Sub-VL contiguous groups (S < physVL and physVL % S == 0, e.g. i32 64/g=2, i16 128/g=2): vci(base) then per-group ∓ g·S + lane-range vsel. Early reject when S neither divides nor is a multiple of phys VL (ODS verify + PTODSL).
  • Deinterleaved / interleaved group results: no dedicated grouped-deint iota path. Require contiguous materialization; unified→legacy rewrites non-contiguous grouped vci/iota to contiguous iota + ensure_layout so layout infer / rematerialize can produce vdintlv etc.

Test plan

  • lit: vmi_to_vpto_iota_group2.pto → shared %idx = pto.vci %base
  • lit: vmi_to_vpto_iota_group_subvl.pto → i32 64/g2, i16 128/g2, i32 128/g4 share
  • lit: vmi_to_vpto_iota_group_deint.pto → shared contiguous vci then vdintlv
  • python3 ptodsl/tests/test_vmi_vci_dynamic_index.py (const / dynamic / group=2 / sub-VL / untileable reject)
  • Simple camodel share case:
    scripts/sim_dsl.sh --output /tmp/vci_vadds_g2_out ptodsl/examples/vci_vadds_share_launch.py -- --groups 2
    vci(0)+vadds(1000)+vsts expects 1000..1063|1000..1063 (g=1: 1000..1063)
  • CI ptodsl / lit as applicable

After rebuild: ninja ptoas_runtime_staging (ptoas loads runtime-staging/lib/ptoas.so).

@peanutchan peanutchan changed the title fix(ptodsl): coerce MLIR index bases to i32 for pto.vmi.vci fix(vmi): dynamic vci index coerce + grouped contiguous iota for camodel Aug 3, 2026
@peanutchan peanutchan changed the title fix(vmi): dynamic vci index coerce + grouped contiguous iota for camodel fix(vmi): dynamic vci index + group-periodic iota (shared index vreg) Aug 3, 2026
@peanutchan peanutchan changed the title fix(vmi): dynamic vci index + group-periodic iota (shared index vreg) fix(vmi): dynamic vci index + group-periodic share (raw vci) Aug 4, 2026
@mouliangyu

mouliangyu commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

当前 group 的合法性约束在 PTODSL / op verifier 与 VMIToVPTO lowering 之间不一致,建议合入前统一。

例如 pto.vmi.vci(pto.i32(0), size=64, group=2)

  • PTODSL 只检查 group > 0size % group == 0,因此接受该调用;
  • VMIVciOp::verify() / VMIIotaOp::verify() 同样只检查整除,因此 IR 验证通过;
  • VMIToVPTO 额外要求 group_size % physical_lanes_per_part == 0。这里 group_size=32、i32 物理 VL 为 64,最终在 lowering 报 failed to legalize operation pto.vmi.iota

请明确并统一接口约束:

  1. 如果 ODS 中声明的任意可整除 group 都应支持,请补齐 sub-VL group-periodic ramp 的 materialization;
  2. 如果当前只支持 group size 为物理 VL 整数倍,请在 PTODSL 和 op verifier 提前拒绝,并给出可执行的诊断,不要让合法性问题延迟到 conversion failure;
  3. 补充 i32 size=64/group=2、i16 size=128/group=2 等边界/negative tests,并确保 PTODSL 与直接 MLIR 两条入口约束一致。

另外,grouped deinterleaved layout 也需要纳入约束或测试;当前 factor=1, part=0 的 materialization 会把两路相同 ramp 交错成 [base, base, base+1, base+1, ...],与声明的 group-periodic 语义不一致。

@peanutchan

Copy link
Copy Markdown
Contributor Author

已按方案 1 补齐,并统一了 PTODSL / ODS verify / VMIToVPTO 的约束(最新 commit d407583ee):

  1. Sub-VL group-periodic:当 S < physVLphysVL % S == 0(如 i32 64/g=2、i16 128/g=2)时,用 vci(base) + 按组 ∓ g·S + lane-range vsel 拼出 group-periodic ramp;S 既不能整除也不能被整除 phys VL 时,PTODSL 与 VMIVciOp/VMIIotaOp::verify 提前拒绝(不再拖到 conversion failure)。
  2. Grouped deinterleaved:不单独做 grouped-deint materialization。grouped vci/iota 只生成 contiguous;若结果 layout 非 contiguous,unified→legacy 改写为 contiguous iota + ensure_layout,由 layout infer 产出 vdintlv 等。这样避免 factor=1, part=0 把两路相同 ramp 交错成 [base,base,base+1,base+1,...]
  3. 测试
    • lit vmi_to_vpto_iota_group_subvl.pto(i32 64/g2、i16 128/g2、i32 128/g4 share)
    • lit vmi_to_vpto_iota_group_deint.pto(shared vcivdintlv
    • PTODSL test_vmi_vci_dynamic_index.py 增加 sub-VL 与 untileable reject

请再看一眼 PR description 与上述 lit。

@peanutchan

Copy link
Copy Markdown
Contributor Author

E2E verification update:

  • Native PTOAS -> Bisheng builds passed for g1, full-VL g2, sub-VL i32_g2, i32_g4, and i16_g2 (each produced an AArch64 kernel.o and launch .so).
  • Ascend950PR_9599 camodel passed all five cases. Each kernel computes vci(0, group=...) + vadds(1000), stores through vsts, DMA-copies to GM, and checks exact host output. Group boundaries restart at 1000 as expected.
  • Grouped VMI lit regressions: 3/3 passed.
  • PTODSL dynamic/group/sub-VL regression: passed.

The launch probes are now committed on this PR branch in 3ae313def.

@mouliangyu

mouliangyu commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

复检 3ae313def 后,sub-VL group 的物化已经补上,但仍有两个 correctness 问题需要合入前处理。

1. [P1] deinterleaved 修复在真实编译流程中可能不会触发

PR 的处理思路是:当 grouped vci/iota 的结果需要 non-contiguous layout 时,先生成 contiguous grouped iota,再用 ensure_layout 转成目标 layout。这个思路本身没有问题。

问题是该 rewrite 与 layout assignment 的执行顺序相反:

VMILowerUnifiedToLegacy   ← 这里执行新增 rewrite
...
VMILayoutAssignment      ← 到这里才决定结果采用什么 layout
VMILayoutRematerialize
...
VMIToVPTO

对应 pipeline 在 tools/ptoas/ptoas.cpp:3024-3051。新增 rewrite 位于 VMILowerUnifiedToLegacy.cpp:1222-1242, 1625-1655,它只有在执行时已经看到 concrete non-contiguous layout 才会插入 contiguous iota + ensure_layout

正常 PTODSL/PTO surface IR 在这个阶段还没有 layout。因此真实流程可能是:

1. lower-unified 看到 grouped iota,但类型尚未分配 layout
   → rewrite 不触发
2. layout assignment 根据 widening/elementwise consumer,
   将这个 grouped iota 分配为 deinterleaved=2
3. 不会再次执行 lower-unified rewrite
4. VMIToVPTO 在 5403-5406 行拒绝 grouped non-contiguous iota
   → legalization/编译失败

这里不是假设 layout assignment 会自动保护 iota:VMILayoutPropagation.cpp:784-786 当前把 VMIIotaOp 当作 free-result-layout producer,允许它直接获得 consumer 请求的 layout。

新增测试 test/lit/vmi_new/vmi_to_vpto_iota_group_deint.pto:19-20 没覆盖这条真实路径。测试提前把结果类型写成了 deinterleaved=2,并且只运行:

-vmi-lower-unified-to-legacy -vmi-to-vpto

这等于提前向 rewrite 提供了 production pipeline 在该阶段尚不知道的信息,所以测试能通过,不能证明从无 layout 的 surface IR 出发也能通过。

建议考虑定义一条独立的内部 legacy op,例如 pto.vmi.group_iota,用于承载 grouped iota 的特殊行为:

public unified API:
  vci(..., group=None)  → legacy iota
  vci(..., group=C)     → legacy group_iota

layout capability:
  iota        → 保持现有普通 iota 的 layout/tail 行为
  group_iota  → 只能直接产生 contiguous layout

这里不需要增加新的 PTODSL 用户 API;对外仍是 pto.vmi.vci(..., group=...)。关键是让 group_iota 在 layout support/propagation 中明确成为 contiguous-only producer,而不是继续复用当前允许任意结果 layout 的 VMIFreeResultLayoutTransfer

这样当 widening/elementwise consumer 请求 deinterleaved=2 时,现有 propagator 会自然把 group_iota 的 primary layout 选为 contiguous,将 consumer 的请求记录为 use conflict,并由 materializeUseConflict() 自动插入 ensure_layout。PR 当前在 VMILayoutRematerialize.cpp:243-250 增加的保护会保留该 ensure,随后 VMIToVPTO 分别 lowering contiguous group_iota 与 layout conversion。

独立 op 也可以把 group 整除约束、full-VL/sub-VL materialization 和 grouped lowering 从普通 iota 的 verifier/lowering 中隔离出来,避免继续在多个阶段用 getGroupAttr() 分支表达两套不同的 producer capability。

同时请增加一个从无 layout 的 !pto.vmi.vreg<...> 输入开始、运行完整 semantic pipeline,并由 widening consumer 自然推导出 deinterleaved layout 的回归测试,确认 group_iota(contiguous) → ensure_layout(deinterleaved) 是由 assignment 自动生成的。

2. [P2] group=1 错误拒绝合法 tail ramp

按本 PR 的语义,group=1 只有一个组,因此应与不写 group 完全等价。例如:

vci(size=100)           → 0..99
vci(size=100, group=1)  → 也应为 0..99

现有 ungrouped lowering 已支持 i32 size=100 的 tail,测试在 test/lit/vmi_new/vmi_to_vpto_iota_tail.pto:12-19

但新约束要求 group_size 与物理 VL 互相整除(VMI.cpp:965-971, 2629-2635ptodsl/ptodsl/_vmi_namespace.py:285-295)。对 i32 size=100, group=1

group_size = 100
physical VL = 64

二者互不整除,因此 PTODSL/verifier 会拒绝这个本应等价于普通 iota 的输入;即使绕过 verifier,grouped lowering 的 physical-result-count 检查也不支持该 tail。

若采用上述内部 op 划分,建议在 unified→legacy lowering 时直接把 group == 1 规范化为普通 iota,只有 group > 1 才生成 group_iota;并增加 size=100, group=1 的 PTODSL 与直接 MLIR 回归。

其他合入条件

  • 用户文档尚未同步 group 参数与 group-periodic 语义:ptodsl/docs/user_guide/14-vmi-virtual-instruction-set.md:420-432docs/isa/vmi-isa/02-index-gen.md:12-39
  • 当前 GitHub 状态仍为 CONFLICTING / DIRTY,最终 head 没有 checks;与当前 main 的内容冲突在 ptodsl/ptodsl/_vmi_namespace.py。请 rebase/resolve 后让完整 CI 在最终 head 上重新运行。

@peanutchan

Copy link
Copy Markdown
Contributor Author

Addressed the two correctness blockers in c38c487d9:

  • P1: added internal contiguous-only pto.vmi.group_iota; layout assignment now produces group_iota(contiguous) -> ensure_layout(deinterleaved) for widening-consumer inference before VPTO lowering.
  • P2: normalized group=1 to ordinary iota, preserving legal tails such as i32 size=100.
  • Added no-layout full-pipeline deinterleaved regression, realistic group=1 tail coverage with vadds/vstore, PTODSL coverage, and updated semantics docs.

Verified:

  • rebuilt pto-test-opt, PTOASCompiler, and PTOASPythonPackage
  • new P1/P2 lit regressions
  • existing grouped iota group=2, sub-VL, and pre-annotated deinterleaved regressions
  • ordinary iota tail/rematerialization/verifier regressions
  • ptodsl/tests/test_vmi_vci_dynamic_index.py

@peanutchan

Copy link
Copy Markdown
Contributor Author

E2E follow-up for c38c487d9:

  • PASS: PTOAS → Bisheng native object/link and ACL camodel numerical execution for group=1 full VL, group=2 share, group=1 i32 size=100 tail, sub-VL i32 group=2/group=4, and sub-VL i16 group=2.
  • All validated outputs matched the expected ramps after vadds (including 1000..1099 for the group=1 tail).
  • Camodel logs confirmed actual AIV execution, not IR-only compilation.

Remaining environment/coverage limitations:

  • The repository sim_dsl.sh + torch_npu/msprof route fails on this host during aclInit (507008) because of the installed CANN/torch_npu library mismatch. Direct ACL camodel hosts work.
  • The automatically inferred deinterleaved widening-consumer path remains IR/FileCheck-only because current PTODSL bindings do not expose the required masked vcvt form and no executable launch example exists.

So grouped share/sub-VL/tail lowering is native/camodel e2e validated; deinterleaved assignment is not yet runtime-covered.

@peanutchan

Copy link
Copy Markdown
Contributor Author

Rebased onto latest main to clear the merge conflict.

Conflict resolved in ptodsl/ptodsl/_vmi_namespace.py by keeping both:

  • IndexType (PR: coerce dynamic MLIR index bases for pto.vmi.vci)
  • UnitAttr (main)

Force-pushed feat/vmi-vci-dynamic-index-base with --force-with-lease.

@peanutchan
peanutchan force-pushed the feat/vmi-vci-dynamic-index-base branch from c38c487 to dc4132d Compare August 5, 2026 06:52
@peanutchan

Copy link
Copy Markdown
Contributor Author

Post-rebase regression at dc4132d14 completed successfully.

Local results:

  • PASS: clean rebuild of pto-test-opt and PTOASPythonPackage
  • PASS: all 10 iota/group lit files (12 RUN pipelines), including group=1 tail, group=2 sharing, sub-VL groups, inferred deinterleaved group_iota -> ensure_layout, rematerialization, and negative verifier cases
  • PASS: ptodsl/tests/test_vmi_vci_dynamic_index.py
  • PASS: PTOAS -> Bisheng native objects/link + ACL camodel numerical validation for all six executable scenarios:
    • group=1 full VL
    • group=2 full-VL share
    • group=1 i32 size=100 tail
    • sub-VL i32 group=2
    • sub-VL i32 group=4
    • sub-VL i16 group=2

Every native case produced ARM kernel.o/launch.o, linked with Bisheng, executed on camodel, and matched the expected output. The inferred deinterleaved widening path remains IR-pipeline coverage because PTODSL still lacks an executable masked vcvt surface for that case.

Tracked working tree is clean. GitHub CI is still pending after the rebase.

peanutchan and others added 9 commits August 5, 2026 16:15
Dynamic loop IVs (TileLang T.serial / scf.for) arrive as MLIR index.
VCI ODS/verify require an integer/float sreg element type; default
index→i32 so dynamic bases lower to VCI Vd, Sn like Ascend S.vci.

Co-authored-by: Cursor <cursoragent@cursor.com>
Dynamic group=2 (VL128) failed on camodel when contiguous iota used
raw VCI with a register Sn base. Restore {group} on vci/iota, keep
group-periodic lane offsets, and materialize contiguous chunks as
vci(0)+vadds like deinterleaved/ASC so dynamic bases rematerialize.

Co-authored-by: Cursor <cursoragent@cursor.com>
group=2 is group-periodic [base..base+S | …], not a continuous 0..L-1
ramp. Physical parts with the same laneOffset reuse one iota chunk so
VL128 group=2 does not re-emit duplicate vci/vadds.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the vci(0)+vadds contiguous materialize path; contiguous iota is
pto.vci(chunkBase) again, with group=2 reusing one SSA. Replace the
topk-heavy share proof with lit expectations plus a simple
vci(0)+vadds(1000)+vsts camodel example.

Co-authored-by: Cursor <cursoragent@cursor.com>
Support group-periodic vci/iota when S < physVL (mask+vsel), and rewrite
non-contiguous grouped results to contiguous iota + ensure_layout so
layout infer handles interleave/deinterleave without a dedicated path.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace unpack-only checks with real consumers: shared group2 + vadds/vsts,
more sub-VL group sizes (i32 g2/g4/g8, i16 g2/g4), and deint + sitofp vcvt.

Co-authored-by: Cursor <cursoragent@cursor.com>
Avoid mixing top-level mlir.ir bindings with ptoas MLIR types in the
untileable group reject check.

Co-authored-by: Cursor <cursoragent@cursor.com>
Exercise full-VL sharing and sub-VL group restart semantics through vadds, vsts, and GM output so native regressions catch lowering gaps.

Co-authored-by: Cursor <cursoragent@cursor.com>
Lower grouped VCI through a contiguous-only internal producer so layout assignment materializes deinterleaved consumers, while normalizing group=1 to ordinary tail-capable iota.

Co-authored-by: Cursor <cursoragent@cursor.com>
@peanutchan
peanutchan force-pushed the feat/vmi-vci-dynamic-index-base branch from dc4132d to dc668b9 Compare August 5, 2026 08:16
@peanutchan

Copy link
Copy Markdown
Contributor Author

Rebased again onto latest main (2ede61b82) after new conflicts appeared from later main merges (notably #1087 / #1119).

Conflict resolved in ptodsl/ptodsl/_vmi_namespace.py by keeping both:

  • vsstb from main
  • grouped vci(..., group=...) from this PR

Force-pushed feat/vmi-vci-dynamic-index-base with --force-with-lease (tip dc668b968).

@mouliangyu

Copy link
Copy Markdown
Collaborator

复检最新 dc668b968:上一轮的两个问题已经修复,group_iota(contiguous) -> ensure_layout(...) 的真实 layout-assignment 路径以及 group=1 tail 都已覆盖。当前还发现下面两个需要处理的问题。

1. [P1] group>1 的逻辑 tail 会通过 API/verifier,但在 VMIToVPTO 必然 legalization 失败

pto.vmi.vci(pto.i32(0), size=32, group=2) 为例:

  • 预期:公开接口既然接受该输入,就应生成 32 个逻辑 lane,即 [0..15 | 0..15],多出的物理 lane 按普通 tail 处理;
  • PTODSL ptodsl/ptodsl/_vmi_namespace.py:312-326 接受它,因为 group_size=16 能整除 i32 物理 VL 64;
  • VMIVciOp::verify() / VMIGroupIotaOp::verify()lib/PTO/IR/VMI.cpp:974-986, 2639-2654)也接受同一约束;
  • contiguous !pto.vmi.vreg<32xi32> 的物理 arity 按 ceil 计算为 1(lib/PTO/IR/VMI.cpp:4301-4328);
  • 但 grouped lowering 又要求 resultTypes.size() * lanesPerPart == logicalLaneslib/PTO/Transforms/VMIToVPTO.cpp:5416-5419),即 1 * 64 == 32,因此 conversion 失败。

size=96, group=3 同样会触发:group_size=32 合法,物理 arity 为 2,但 2 * 64 != 96

建议把契约统一为支持 grouped logical tail:去掉“物理容量必须恰好等于逻辑长度”的限制,让最后一个物理 chunk 的超出 lane 沿用普通 tail 的 inactive/padding 处理;createSubVLGroupPeriodicChunk() 已能生成所需周期。请增加至少 i32 size=32/group=2(不足一个 VL)和 size=96/group=3(末尾 partial VL)的完整 pipeline 测试,并通过实际 consumer/store 验证 tail mask。若实现明确不支持这类 tail,则必须反向在 PTODSL、两个 verifier 和文档中提前拒绝,不能保留 accepted-then-conversion-failure。

2. [P2] group_size=1 会把一个 broadcast 展开成 O(physical VL) 的指令链

createSubVLGroupPeriodicChunk()lib/PTO/Transforms/VMIToVPTO.cpp:5257-5303 对每个物理 chunk 内 group 逐个生成 mask + vsel,除第 0 组外还生成 vadds。例如合法的 i8 size=256, group=256group_size=1)会生成约 256 个 lane-range mask、256 个 vsel 和 255 个 vadds,还不含初始化;但其语义只是每个 lane 都等于 base

建议在该 helper 中直接 special-case groupSize == 1 为一次 vdup(base),并增加极端 group-count 的 lowering 测试,确保不再出现 vsel/vadds 链。否则一个看似简单且公开允许的 vci 会造成显著 code size、编译时间和运行时开销。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants